/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package prefuse.vensim;
import com.vensim.Vensim;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
//import java.util.Vector;



//import org.apache.log4j.Logger;
//import org.apache.log4j.PropertyConfigurator;
/**
 *
 * @author Wenyi An
 */
public class VensimController {



    private double decisionTreeStartTime;   // may be different than the model start time
    private String modelFileName;
    private String vdfFileName;
    private Vensim venControl;
    private double curTime;
    private String simulationName;
    private int branchNumber;
    private int counter;
    private int ctxId;
    private int x;
    private static double getvalue;
    
    private static String varName = "birth rate";
    private static String ann="ann";
    private static final String V_RUNNAME = "c:\\tmp\\rabbitann.vdf";
    private static final String V_SET_RUNNAME = "SIMULATE>RUNNAME|" + V_RUNNAME;
    private static final String V_VRUNNAME = getvalue + ":= SIMULATE>GET VDF CONSTANTS("+V_RUNNAME+","+varName+","+"5)";
    private static final String V = "MENU>VDF2XLS";
    
   
    /**
     * Command for running a model.
     */
    private static final String V_RUN = "MENU>RUN|o";
    /**
     * Command used to tell vensim to opearte only in memory without using files.
     */
    private static final String V_MEMFILE = "FILE>MEMFILE|1";
    /**
     * Command used to load model.
     */
    private static final String V_LOAD_MODEL = "SPECIAL>LOADMODEL|";
    
    /**
     * constructor, creates the VensimActionStack
     */
    public VensimController()
    {
    
    	 venControl= new Vensim("VenDLL32"); // ****TODO:  should be reset to vendll32
    	
    }

    /**
     * Sets up the simulation using the Decision Tree and set up the initial values
     * @param tree, startTime
     */
    public void LoadModel()
    {
//        String loadModelCommand;
//        String loadModelCommand1;
//  //      this.tree = theTree;
//        this.branchNumber = 0;
//
//   // 	logger.debug("Initializing tree");
//        
        modelFileName = "C:\\tmp\\RABBIT.vpm";
//       
//        loadModelCommand =  "SPECIAL>LOADMODEL|"+modelFileName;
//       
//        long time = 37989;
        

        try {
			doCommand(V);
//			 doCommand(V_MEMFILE);
//		        doCommand(V_SET_RUNNAME);
//		        run();
		} catch (VensimException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        
        // create new context that will be removed by a call to end() method
//        ctxId = Vensim.ContextAdd(1);
//        if (ctxId == 0) {
//           // throw new VensimException("Can't create new vensim context");
//        }
        //	  loadModel(modelPath);
 //       	long ann= 0;
 //       	  doCommand(V_MEMFILE);
//			doCommand(V_SET_RUNNAME);
//    	venControl.command(V_LOAD_MODEL + modelFileName);
//			x=venControl.command(V_VRUNNAME);
//			System.out.println("hahhha"+x);
//			
//			x = venControl.command(V_RUN);
//			doCommand(V1);
//			run();
//			 String venCommand = "MENU>SETVAL|" + ann + " = " + "GET VDF CONSTANTS("+V_RUNNAME+","+varName+","+"37989)";
//			 
//			 doCommand(venCommand);
//			float[] val = new float[20];
//	        int x = venControl.get_val("ann",val);
//	        System.out.println("^^^Variable Value --ann : "+val[0]);
//        System.out.println("hahhha"+x);

        if (true)
        	{
        	// ****comment out after testing
//	        System.out.println("++++++++"+loadModelCommand+"+++++++");
//	
//	        this.venControl.command(loadModelCommand);
        	
        	
	        System.out.println("++++++++"+V_VRUNNAME+"+++++++");
	
	        //	        this.venControl.command( "MENU>RUN");
	   //    String varnames[] = this.venControl.get_varnames("*",0);
	        String varnames[] = getVariables();
	        for(int i=0; i<varnames.length;i++) {
	            System.out.println("****"+varnames[i]+"****");
	        }
        	}
      
//        this.testMethod();
//    	logger.debug("Finished initializing tree");
    }
    
    
    /**
     * Returns values for variable with given name. Returns empty 
     * array when variable wasn't found.
     * 
     * @param name variable name
     * @param maxVal maximal number of values that should be retrieved
     * @return array of variable values (can be empty)
     * @throws VensimException in case of any Vensim error
     */
    public float[] getVariable(String name, int maxVal) throws VensimException {
        validateContext();

        float[] varVal = new float[maxVal];
        float[] timeVal = new float[maxVal];
        
 //       log.debug("Looking for value of variable " + name);
        int ret = Vensim.CGetData(ctxId, V_RUNNAME, name, "", varVal, timeVal, maxVal);
        if (ret == 0) {
  //          log.debug("VensimVariable " + name + " wasn't found");
            return new float[0]; 
        }
    //    log.debug("Found " + ret + " values for variable " + name);
        return Arrays.copyOfRange(varVal, 150, ret);
    }
    
    public String[] getVariables() {
        return Vensim.CGetVarNames(ctxId, "*", Vensim.VARTYPE_ALL);
    }
    /**
     * Executes given command against Vensim core.
     * 
     * @param command command that should be executed
     * @throws VensimException in case of any Vensim error
     */
    private void doCommand(String command) throws VensimException {
        int ret = venControl.CCommand(ctxId, command);
  
        System.out.println("ffffffffffffff"+ret+"     "+ctxId);
        if (ret == 0) {

            throw new VensimException("Error when executing command: " + command);
        }


        
    }
    /**
     * Checks if Vensim context was properly initialized, throws
     * exception if it wasn't
     * @throws VensimException if context wasn't properly initialized.
     */
    private void validateContext() throws VensimException { 
        if (ctxId <= 0) {
            throw new VensimException("There is no valid Vensim context available, trying to reuse the same instance twice?");
        }
    }
    
    /**
     * Runs the simulation.
     * @throws VensimException in case of any Vensim error
     */
    public void run() throws VensimException {
        validateContext();

 //       log.debug("Running the model");
        doCommand(V_RUN);
    }
    /**
     * Loads given model.
     * @param modelPath path to model file that should be loaded
     * @throws VensimException in case of any Vensim error
     */
//    private void loadModel(String modelPath) throws VensimException {
//        doCommand(V_LOAD_MODEL + modelPath);
//        
//        String[] name = Vensim.CGetInfo(ctxId, Vensim.INFO_MODELNAME);
// //       log.debug("Loaded model " + name[0]);
//    }
//    /**
//     * retrieve the list of varnames in an iterator.7
//     * @return
//     */
//    public Iterator<String> getVariableNames()
//    {
//        LinkedList<String> varNameList = new LinkedList<String>();
//        String varnames[] = this.venControl.get_varnames("*",0);
//
//        for(int i =0; i<varnames.length; i++)
//        {
//            varNameList.add(varnames[i]);
//        }
//        return varNameList.iterator();
//    }
    /**
     * retrieve the list of varnames in an iterator.7
     * @return
     */
//    public String[] getVariableNamesbyArray()
//    {
//       
//        String varnames[] = this.venControl.get_varnames("*",0);       
//        return varnames;
//    }
    /**
     * retrieve the list of constant parameters in an iterator.7
     * @return
     */
//    public Iterator<String> getConstantParameterNames()
//    {
//        LinkedList<String> varNameList = new LinkedList<String>();
//        String varnames[] = this.venControl.get_varnames("*",Vensim.VARTYPE_CONSTANT);
//
//        for(int i =0; i<varnames.length; i++)
//        {
//            varNameList.add(varnames[i]);
//        }
//        return varNameList.iterator();
//    }

    /**
     * begin the simulation. Iterates the tree with dfs and use each of the edges to create an
     * ModelingAgentObject and insert it to the action Stack. When it reaches a Terminal Node
     * it calls runSimulation
     * 
     * For simplicity, we just assume that the start time is the start time of the model.  Extra choices will likely confuse the user.
     */
//    public void startSimulation(String theSimulationName /*, double theDecisionTreeStartTime */)
//    {
////        this.decisionTreeStartTime = theDecisionTreeStartTime; 
//        this.simulationName = theSimulationName;
//   //     TreeNode curNode = this.tree.getRootNode();
//  //      ModelingAgentObjectInterface rootObject = this.createModelingAgentObject(curNode.getVariableName(), Double.NEGATIVE_INFINITY, curNode.getTime());
//  //      this.push(rootObject);
//    //    setSeed();
// //       startSimulationRecurseHelper(curNode);
//    }
//    
    /**start to set up the random seed**/
//    public void setSeed(){
//		String str;
//		InputStreamReader stdin = new InputStreamReader(System.in);
//		BufferedReader bufin = new BufferedReader(stdin);
//		try {
//			System.out.print("please in put the seed");
//			str = bufin.readLine();
//			System.out.println("The seed you input in is " + str);
//		} catch (IOException E) {
//			System.out.println("the I/O errors has happend");
//		}
//    }

    /**
     * recursive function used to help startSimulation to recurse through the tree
     * using a dfs
     */
//    protected void startSimulationRecurseHelper(TreeNode theCurNode)
//    {
//        // if the curNode is a terminal node, run simulation
//        if(theCurNode.getNodeType().equals("TerminalNode"))
//        {
//   //         TerminalNodeInterface curNode = (TerminalNodeInterface) theCurNode;
////            ModelingAgentObjectInterface curModelingAgentObject = this.createModelingAgentObject(curNode.getVariableName(), Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
////            this.vensimActionStack.push(curModelingAgentObject);
//            curNode.setRollBackValue(this.runSimulation(curNode.getVariableName()));
//        }
//        // if the curNode is not a terminal Node it must be an IntermediateTreeNode
//        else
//        {
//            // cast to intermediate Node and grab its child edges
//     //       IntermediateTreeNode curNode = (IntermediateTreeNode) theCurNode;
//    //        Iterator<Edge> edgeList = curNode.getChildEdgeList();
//
//            // for each child edge, create a modelingAgentObject from it, push it on the stack
//            // and recurse. After recursion pop off the node to backtrack.
////            while(edgeList.hasNext())
////            {
////                Edge curEdge = edgeList.next();
////                ModelingAgentObjectInterface curModelingAgentObject = this.createModelingAgentObject(curEdge);
////                this.vensimActionStack.push(curModelingAgentObject);
////                this.startSimulationRecurseHelper(curEdge.getDestinationNode());
////                this.pop();
////            }
//        }
//    }

    /**
     * retrieves the iterator of the modelingAgentActionStack
     * @return
     */
//    public Iterator<ModelingAgentObjectInterface> getModelingAgentActionStack()
//    {
//        return this.vensimActionStack.getList();
//    }

    /**
     * creates a modelingAgentObjectInterface with the given edge
     * @param Edge
     * @return
     */
//    public ModelingAgentObjectInterface createModelingAgentObject(Edge theEdge)
//    {
//        String varName;
//        double value;
//        double time;
//        VensimObject venObject;
//
//        varName = theEdge.getSourceNode().getVariableName();
//        value = theEdge.getValue();
//        time = theEdge.getDestinationNode().getTime();
//
//        venObject = new VensimObject(varName, value, time);
//
//        return venObject;
//    }

    /**
     * creates a modelingAgentObjectInterface with the given edge
     * @param Edge
     * @return
     */
//    protected ModelingAgentObjectInterface createModelingAgentObject(String varName, double value, double time)
//    {
//        VensimObject venObject = new VensimObject(varName, value, time);
//
//        return venObject;
//    }

    /**
     * pushes a ModelingAgentObject to the action Stack
     * @param modelingAgentObject
     */
//    public void push(ModelingAgentObjectInterface modelingAgentObject)
//    {
//        this.vensimActionStack.push(modelingAgentObject);
//    }

    /**
     * pop off a ModelingAgentObject from the action Stack
     * @return
     */
//    public ModelingAgentObjectInterface pop()
//    {
//        return this.vensimActionStack.pop();
//    }

    /**
     * Run the simulation with the current ActionStack
     * @return
     */
//    protected double runSimulation(String terminalVarName)
//    {
//        double timeAtEndOfIntervalAfterSetting;
//        double val;
//        String varName = "";
//
//        this.branchNumber++;
//        String simName = this.simulationName + "_Branch_" + this.branchNumber;
////    	logger.trace("In VensimSimulationController.runSimulation, initializing output run named " + simName);
////        this.resetGame(this.getFinalTime(), simName);
//        // retrieve the list of vensimActionStack
////        ModelingAgentObjectInterface curModelAgentObject;
////        Iterator<ModelingAgentObjectInterface> actionStackIter = this.vensimActionStack.getList();
//
//        // iterates through the actionStack
////        while(actionStackIter.hasNext())
////        {
////            // for each action, set the new game time interval and setVal
////            // then call GameOn to run the simulation to that point.
////            curModelAgentObject = actionStackIter.next();
////
////            timeAtEndOfIntervalAfterSetting = curModelAgentObject.getTime();
////            
////            // obtain the current value of the time (the time at the beginning of interval) according to vensim
////            double vensimTime = getVenVarValue("time")[0];
////            double timePrecisionDemanded = 1E-5;		// we will tolerate small errors in time
////            
////            val = curModelAgentObject.getValue();
////            varName = curModelAgentObject.getVariableName();
////
////        	logger.trace("In VensimSimulationController.runSimulation, at " + vensimTime + ") setting variable " + varName + " to value " + val);
//////            System.out.println("Simulating: TIME: " + time + " VAL: " + val + "VarName: " + varName);
////            this.vensimSetGameAdvanceInterval(timeAtEndOfIntervalAfterSetting);
////            this.vensimSetVal(varName, val);
////            this.vensimAdvanceByGameIntervalViaGameON();
////        }
//
//        // return the value by calling get val
//        float[] x = getVenVarValue(terminalVarName);
//
//        double vensimTime = getVenVarValue("time")[0];
//   // 	logger.trace("Final value for terminal node at time " + vensimTime + "=" + x[0]);
//        this.vensimEndGame();
//  //  	logger.trace("In VensimSimulationController.runSimulation, finished output run named " + simName);
//        return x[0];
//    }

    /**
     * Uses the vensim command to setVal for the specified varName and value
     * @param varName - variable name
     * @param value - value for the varName
     */
//    protected boolean vensimSetVal(String varName, double value)
//    {
//        // if the values are null, that means it is the first node and nothing
//        // needs to be set.
//        if(varName == null || value == Double.NEGATIVE_INFINITY)
//        {
//           return true;
//        }
//        String venCommand = "SIMULATE>SETVAL|" + varName + " = " + value;
//        int result = venControl.command( venCommand );
//        if (result == 0)
//        {
//  //      	logger.error("Failed in VensimSimulationController.vensimSetVal, setting variable" + varName + " to value " + value);
//            System.err.println(venCommand + " -- failed" );
//            return false;
//        }
//        return true;
//    }

    /**
     * Uses te vensim command to set the GameInterval for the next node
     * @param time
     */
//    protected boolean vensimSetGameAdvanceInterval(double newTime)
//    {
//        double deltaTime = newTime - this.curTime;
//
//  //  	logger.trace("In VensimSimulationController.vensimSetGameAdvanceInterval, attempting to advance by time interval " + deltaTime);
//
//        String venCommand = "GAME>GAMEINTERVAL|" + deltaTime;
//        int result = venControl.command( venCommand );
//
//
//        // if error occured
//        if (result == 0)
//        {
//            System.err.println(venCommand + " -- failed" );
//            return false;
//        }
//        // if no error, set newTime as curTime
//        else
//        {
//            this.curTime = newTime;
//            return true;
//        }
//    }

    /**
     * Uses the vensim command GameOn to move to the next game interval
     */
//    protected boolean vensimAdvanceByGameIntervalViaGameON()
//    {
//        counter++;
////        System.out.println("Counter : " + counter);
//        int result = venControl.command("GAME>GAMEON");
//
//        // if error occured
//        if (result == 0)
//        {
//    //    	logger.error("Failed in VensimSimulationController.vensimAdvanceByGameIntervalViaGameON");
//            System.err.println("GAME>GAMEON-- failed" );
//            return false;
//        }
//        return true;
//    }
//
//    /**
//     * Uses the vensim command GameOn to move to the next game interval
//     */
//    protected boolean vensimEndGame()
//    {
//        int result = venControl.command("GAME>ENDGAME");
//
//        // if error occured
//        if (result == 0)
//        {
//            System.err.println("GAME>ENDGAME-- failed" );
//            return false;
//        }
//        return true;
//    }

    /**
     * resets the game to allow to simulate a new actionStack
     * this needs to be refactor... it should reset tree without reloading a model
     */
//    protected void resetGame(double finalTime, String simName)
//    {
//        String loadModelCommand =  "SPECIAL>LOADMODEL|"+modelFileName;
//        venControl.command(loadModelCommand);
//        venControl.command("SIMULATE>SETVAL|FINAL TIME = "+ finalTime);
////        venControl.command("SIMULATE>SETVAL|initial population = 500");
////        venControl.command("SIMULATE>SETVAL|intial carrying capacity = 500");
////        venControl.command("SIMULATE>SETVAL|average lifetime = 9");
////        venControl.command("SIMULATE>SETVAL|birth rate initial = 0.23");
//        venControl.command("SIMULATE>RUNNAME|"+ simName);
//        venControl.command("MENU>GAME");

	    // set the curtime as the start Time of the MODEL, so the simulation to the initial node of the tree knows to advance this to the first node in the tree
//        this.curTime = getModelStartTime();
//        //        venControl.command("SIMULATE>SETVAL|INITIAL TIME = "+ this.decisionTreeStartTime);
//    }
//    protected double getModelStartTime()
//    {
//        // get the start time of the model by reading a variable
//	    float[] arrayModelStartTime = getVenVarValue("INITIAL TIME");
//	    
//	    return arrayModelStartTime[0];
//    }
    // deprecated -- likely not required
    /*
    protected void advanceFromModelStartToDecisionTreeStartTime()
    {
	    // get the current initial time
	    float[] arrayModelStartTime = getVenVarValue("INITIAL TIME");
	    double modelStartTime = arrayModelStartTime[0];
	    
	    // figure out the difference between the model start time and the decision tree start time
	    double timeFromModelBeginUntilDecisionTreeStart = this.decisionTreeStartTime - modelStartTime;

	    // ok, now advance by this length of time
	    assert(timeFromModelBeginUntilDecisionTreeStart >= 0);

	    // if we have to advance by some non-zero time, do so 
	    if (timeFromModelBeginUntilDecisionTreeStart > 0)
	    {
		    this.vensimSetGameAdvanceInterval(timeFromModelBeginUntilDecisionTreeStart);
	        this.vensimAdvanceByGameIntervalViaGameON();
	    }

	    // set the curtime as the start Time of the decision tree
        this.curTime = this.decisionTreeStartTime;

    }
    */
    
    /**
     * retrieves the time variable for the last
     * @return
     */
//    protected double getFinalTime()
//    {
//   //     return this.vensimActionStack.getFinalTime();
//    }

//    public float[] getVenVarValue(String varName){
//        float[] val = new float[20];
//        int x = venControl.get_val(varName,val);
//        System.out.println("^^^Variable Value --"+varName+" : "+val[0]);
//        return val;
//    }
    public static void main(String argv[]) {
    	
//    	 VensimController VensimCtrl = new VensimController();
//    	 VensimCtrl.LoadModel();
//    
    	
    	try {
			vensimHelp vh = new vensimHelp("VenDLL32","C:\\tmp\\RABBIT.vpm");
		} catch (VensimException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    }

}
